﻿using System;

namespace CatEars.Core.Console
{
    /// <summary>
    /// 带颜色的System.Console读写
    /// </summary>
    public static partial class ColorConsole
    {
        #region ConsoleReadWrite

        /// <summary>
        /// Console读写接口
        /// </summary>
        public interface IConsoleReadWrite
        {
            /// <summary>
            /// 将msg写入Console
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            void Write(string msg, ConsoleColor? foreColor = null, ConsoleColor? backColor = null);
            /// <summary>
            /// 将msg写入Console一行
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            void WriteLine(string msg, ConsoleColor? foreColor = null, ConsoleColor? backColor = null);
            /// <summary>
            /// 将msg写入Console
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="args">格式化参数</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            void Write(string msg, string[] args, ConsoleColor? foreColor = null, ConsoleColor? backColor = null);
            /// <summary>
            /// 将msg写入Console一行
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="args">格式化参数</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            void WriteLine(string msg, string[] args, ConsoleColor? foreColor = null, ConsoleColor? backColor = null);

            /// <summary>
            /// System.Console.Read 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            int Read(ConsoleColor? foreColor = null, ConsoleColor? backColor = null);
            /// <summary>
            /// System.Console.ReadKey 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            ConsoleKeyInfo ReadKey(ConsoleColor? foreColor = null, ConsoleColor? backColor = null);
            /// <summary>
            /// System.Console.ReadLine 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            string ReadLine(ConsoleColor? foreColor = null, ConsoleColor? backColor = null);

            /// <summary>
            /// 输出提示文本并获取一行输入
            /// </summary>
            /// <param name="msg">提示文本</param>
            /// <param name="inputAtNewLine">是否在提示文本之后输出换行</param>
            /// <param name="wForeColor">输出文本前景色</param>
            /// <param name="wBackColor">输出文本背景色</param>
            /// <param name="rForeColor">输入内容前景色</param>
            /// <param name="rBackColor">输入内容背景色</param>
            /// <returns></returns>
            string AskInput(string msg, bool inputAtNewLine, ConsoleColor? wForeColor = null, ConsoleColor? wBackColor = null,
                ConsoleColor? rForeColor = null, ConsoleColor? rBackColor = null);
        }

        /// <summary>
        /// 单线程读写操作
        /// </summary>
        public class ConsoleRW : IConsoleReadWrite
        {
            /// <summary>
            /// 备份原来Console的颜色设置  搭配SetColor使用
            /// </summary>
            /// <returns></returns>
            protected ConsoleColor[] BackupColor()
            {
                return new ConsoleColor[] { System.Console.ForegroundColor, System.Console.BackgroundColor };
            }
            /// <summary>
            /// 还原Console的颜色设置  搭配BackupColor使用
            /// </summary>
            /// <param name="colors">【NotNull】调用BackupColor得到的对象 长度为2的数组</param>
            protected void SetColor(ConsoleColor[] colors)
            {
                System.Console.ForegroundColor = colors[0];
                System.Console.BackgroundColor = colors[1];
            }

            /// <summary>
            /// 将msg写入Console
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            public virtual void Write(string msg, ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                var backupColors = BackupColor();
                try
                {
                    System.Console.ForegroundColor = foreColor ?? ColorConsole.DefaultColor;
                    System.Console.BackgroundColor = backColor ?? ColorConsole.DefaultBackColor;
                    System.Console.Write(msg);
                }
                finally
                {
                    SetColor(backupColors);
                }
            }

            /// <summary>
            /// 将msg写入Console一行
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            public void WriteLine(string msg, ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                Write(msg + ColorConsole.NewLine, foreColor, backColor);
            }
            /// <summary>
            /// 将msg写入Console
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="args">格式化参数</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            public void Write(string msg, string[] args, ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                string msg0 = string.Format(msg, args);
                Write(msg0, foreColor, backColor);
            }
            /// <summary>
            /// 将msg写入Console一行
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="args">格式化参数</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            public void WriteLine(string msg, string[] args, ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                string msg0 = string.Format(msg, args);
                WriteLine(msg0, args, foreColor, backColor);
            }

            /// <summary>
            /// System.Console.Read 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            public virtual int Read(ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                var backupColors = BackupColor();
                try
                {
                    System.Console.ForegroundColor = foreColor ?? ColorConsole.InputColor;
                    System.Console.BackgroundColor = backColor ?? ColorConsole.InputBackColor;
                    return System.Console.Read();
                }
                finally
                {
                    SetColor(backupColors);
                }
            }
            /// <summary>
            /// System.Console.ReadKey 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            public virtual ConsoleKeyInfo ReadKey(ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                var backupColors = BackupColor();
                try
                {
                    System.Console.ForegroundColor = foreColor ?? ColorConsole.InputColor;
                    System.Console.BackgroundColor = backColor ?? ColorConsole.InputBackColor;
                    return System.Console.ReadKey();
                }
                finally
                {
                    SetColor(backupColors);
                }
            }
            /// <summary>
            /// System.Console.ReadLine 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            public virtual string ReadLine(ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
            {
                var backupColors = BackupColor();
                try
                {
                    System.Console.ForegroundColor = foreColor ?? ColorConsole.InputColor;
                    System.Console.BackgroundColor = backColor ?? ColorConsole.InputBackColor;
                    return System.Console.ReadLine();
                }
                finally
                {
                    SetColor(backupColors);
                }
            }

            /// <summary>
            /// 输出提示文本并获取一行输入
            /// </summary>
            /// <param name="msg">提示文本</param>
            /// <param name="inputAtNewLine">是否在提示文本之后输出换行</param>
            /// <param name="wForeColor">输出文本前景色</param>
            /// <param name="wBackColor">输出文本背景色</param>
            /// <param name="rForeColor">输入内容前景色</param>
            /// <param name="rBackColor">输入内容背景色</param>
            /// <returns></returns>
            public virtual string AskInput(string msg, bool inputAtNewLine, ConsoleColor? wForeColor = null, ConsoleColor? wBackColor = null,
                ConsoleColor? rForeColor = null, ConsoleColor? rBackColor = null)
            {
                var msg0 = msg;
                if (inputAtNewLine) msg0 += ColorConsole.NewLine;
                Write(msg0, wForeColor ?? InputAskColor, wBackColor ?? InputAskBackColor);
                return ReadLine(rForeColor, rBackColor);
            }
        }

        /// <summary>
        /// 多线程读写操作
        /// </summary>
        public class ConsoleRWSync : ConsoleRW
        {
            //TODO 这里最好使用队列来处理 暂时用锁解决
            object _syncWrite = new object();
            object _syncRead = new object();

            /// <summary>
            /// 将msg写入Console
            /// </summary>
            /// <param name="msg">写入内容</param>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            public override void Write(string msg, ConsoleColor? foreColor = default(ConsoleColor?), ConsoleColor? backColor = default(ConsoleColor?))
            {
                lock (_syncWrite)
                {
                    base.Write(msg, foreColor, backColor);
                }
            }

            /// <summary>
            /// System.Console.Read 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            public override int Read(ConsoleColor? foreColor = default(ConsoleColor?), ConsoleColor? backColor = default(ConsoleColor?))
            {
                lock (_syncRead)
                {
                    return base.Read(foreColor, backColor);
                }
            }
            /// <summary>
            /// System.Console.ReadKey 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            public override ConsoleKeyInfo ReadKey(ConsoleColor? foreColor = default(ConsoleColor?), ConsoleColor? backColor = default(ConsoleColor?))
            {
                lock (_syncRead)
                {
                    return base.ReadKey(foreColor, backColor);
                }
            }
            /// <summary>
            /// System.Console.ReadLine 带颜色的版本
            /// </summary>
            /// <param name="foreColor">前景色</param>
            /// <param name="backColor">背景色</param>
            /// <returns></returns>
            public override string ReadLine(ConsoleColor? foreColor = default(ConsoleColor?), ConsoleColor? backColor = default(ConsoleColor?))
            {
                lock (_syncRead)
                {
                    return base.ReadLine(foreColor, backColor);
                }
            }
        }

        /// <summary>
        /// 开放IConsoleReadWrite对象用于直接操作Console
        /// </summary>
        public static IConsoleReadWrite ConsoleReadWrite { get; private set; } = new ConsoleRWSync();
        #endregion

        #region Config

        /// <summary>
        /// 是否启动同步锁，多线程操作需要设置为true，默认为true
        /// </summary>
        public static bool SyncWrite
        {
            get { return ConsoleReadWrite is ConsoleRWSync; }
            set
            {
                if (SyncWrite != value)
                {
                    if (value)
                    {
                        ConsoleReadWrite = new ConsoleRWSync();
                    }
                    else
                    {
                        ConsoleReadWrite = new ConsoleRW();
                    }
                }
            }
        }

        /// <summary>
        /// 默认文本颜色备份（初次加载CColorConsole类时Consle的颜色）
        /// </summary>
        public static ConsoleColor DefaultColor = System.Console.ForegroundColor;
        /// <summary>
        /// 默认背景颜色备份（初次加载CColorConsole类时Consle的颜色）
        /// </summary>
        public static ConsoleColor DefaultBackColor = System.Console.BackgroundColor;
        
        /// <summary>
        /// 换行字符串（单独配置）
        /// </summary>
        public static string NewLine { get; set; } = "\r\n";
        
        #region 跟日志一样输出时设置五级颜色 + Success的颜色
        /// <summary>
        /// 调试信息颜色
        /// </summary>
        public static ConsoleColor WriteDebugColor = ConsoleColor.DarkGray;
        /// <summary>
        /// 一般信息颜色
        /// </summary>
        public static ConsoleColor WriteInfoColor = ConsoleColor.White;
        /// <summary>
        /// 警告信息颜色
        /// </summary>
        public static ConsoleColor WriteWarnColor = ConsoleColor.Yellow;
        /// <summary>
        /// 错误信息颜色
        /// </summary>
        public static ConsoleColor WriteErrorColor = ConsoleColor.Red;
        /// <summary>
        /// 严重错误信息颜色
        /// </summary>
        public static ConsoleColor WriteFatalColor = ConsoleColor.Magenta;
        /// <summary>
        /// 成功信息颜色
        /// </summary>
        public static ConsoleColor WriteSuccessColor = ConsoleColor.Green;
        #endregion

        #region 询问输入的颜色设置
        /// <summary>
        /// 询问用户输入时的文本颜色
        /// </summary>
        public static ConsoleColor InputAskColor = ConsoleColor.Cyan;
        /// <summary>
        /// 询问用户输入时的背景颜色
        /// </summary>
        public static ConsoleColor InputAskBackColor = ConsoleColor.Black;
        /// <summary>
        /// 用户输入时的文本颜色
        /// </summary>
        public static ConsoleColor InputColor = ConsoleColor.White;
        /// <summary>
        /// 用户输入时的背景颜色
        /// </summary>
        public static ConsoleColor InputBackColor = ConsoleColor.DarkGray;
        #endregion

        #endregion

        #region Input

        /// <summary>
        /// 输出提示文本并获取一行输入
        /// </summary>
        /// <param name="msg">提示文本</param>
        /// <param name="inputAtNewLine">是否在提示文本之后输出换行</param>
        /// <returns></returns>
        public static string AskInput(string msg, bool inputAtNewLine = false)
        {
            return ConsoleReadWrite.AskInput(msg, inputAtNewLine, InputAskColor, InputAskBackColor, InputColor, InputBackColor);
        }

        /// <summary>
        /// 输出提示文本并获取一行输入，自动转换为特定类型
        /// </summary>
        /// <param name="msg">提示文本</param>
        /// <param name="inputAtNewLine">是否在提示文本之后输出换行</param>
        /// <returns></returns>
        public static T AskInput<T>(string msg, bool inputAtNewLine = false)
        {
            Type t = typeof(T);
            msg += "(" + t.Name + ")";
            string strValue = AskInput(msg);
            return (T)Convert.ChangeType(strValue, t);
        }

        /// <summary>
        /// 输出提示文本并获取一行输入
        /// </summary>
        /// <param name="msg">提示文本</param>
        /// <param name="value">【输出】输入的内容</param>
        /// <param name="showTypeTip">是否根据类型T生成输入提示</param>
        /// <param name="inputAtNewLine">是否在提示文本之后输出换行</param>
        /// <param name="intRetry">输入错误时重试次数</param>
        /// <returns>true:成功输入并转换</returns>
        public static bool AskInput<T>(string msg, out T value, bool showTypeTip = true, bool inputAtNewLine = false, int intRetry = -1)
        {
            value = default(T);
            Type t = typeof(T);
            if (showTypeTip)
            {
                string inputTypeTip = "(" + t.Name + ")";
                msg += inputTypeTip;
            }
            intRetry++;
            do
            {
                string strValue = AskInput(msg, inputAtNewLine);
                try
                {
                    value = (T)Convert.ChangeType(strValue, t);
                    return true;
                }
                catch
                {
                    WriteWarnLine("输入的内容有误，请重新输入");
                    intRetry--;
                }
            }
            while (intRetry != 0);
            return false;
        }

        #endregion
    }
}
